package com.zlcx.tz.live.web;

import com.zlcx.tz.live.client.UserClient;
import com.zlcx.tz.live.client.UserFriendClient;
import com.zlcx.tz.live.client.vo.UserFollowInfo;
import com.zlcx.tz.live.client.vo.UserFriendVO;
import com.zlcx.tz.live.client.vo.UserFriendsCondition;
import com.zlcx.tz.live.common.annotation.AutoLog;
import com.zlcx.tz.live.constant.YN;
import com.zlcx.tz.live.domain.TimeLearnRoomDO;
import com.zlcx.tz.live.domain.TimeLearnRoomSeatsDO;
import com.zlcx.tz.live.domain.TimeLearnRoomUsersDO;
import com.zlcx.tz.live.exception.RoomSeatException;
import com.zlcx.tz.live.exception.RoomSeatUsedException;
import com.zlcx.tz.live.exception.UnRoomUserException;
import com.zlcx.tz.live.exception.UserTimeLearningException;
import com.zlcx.tz.live.service.TimeLearnRoomDialogStore;
import com.zlcx.tz.live.service.TimeLearnRoomService;
import com.zlcx.tz.live.vo.*;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
import static com.zlcx.tz.live.constant.ResultStatus.*;
import static com.zlcx.tz.live.vo.UserVO.StatusEnums.LEARN_TIME_LEARNING;
import static com.zlcx.tz.live.vo.UserVO.StatusEnums.NOMAL;

/**
 * @author: anjl
 * @data: 2020-09-30
 **/
@Api(tags = {"计时房间管理API"}, description = "计时房间管理API")
@ApiResponses(value = {
        @ApiResponse(code = 0, message = "OK"),
        @ApiResponse(code = 10401, message = "Unauthorized")
})
@RestController
@Slf4j
@RequestMapping("/timeLearnRoom")
public class TimeLearnRoomController extends BaseController {

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时房间类别-APP", notes = "计时房间类别")
    @GetMapping(value = "/list/types")
    public ResultVO<List<Map<Integer, String>>> listRoomTypes() {

        List<Map<Integer, String>> list = Arrays.stream(TimeLearnRoomDO.TimeLearnRoomType.values())
                .map(t -> {
                    Map<Integer, String> map = new HashMap<>();
                    map.put(t.getCode(), t.getDesc());
                    return map;
                }).collect(Collectors.toList());
        return successState(list);
    }

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时房间列表-APP", notes = "计时房间列表")
    @GetMapping(value = "/list")
    public ResultVO<List<TimeLearnRoomVO>> listRoom(@ApiParam(value = "房间类别", required = true) @RequestParam Integer type) {
        List<TimeLearnRoomVO> list = timeLearnRoomService.list(type);
        return successState(list);
    }

    @ApiOperation(value = "立即加入计时自习室-APP")
    @ApiResponses(value = {
            @ApiResponse(code = 19510, message = "计时自习室用户已满")
    })
    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @GetMapping(value = "/joinNow")
    public ResultVO<TimeLearnRoomVO> joinNow(@ApiParam(value = "计时自习室类型", required = true) @RequestParam Integer type) {

        List<TimeLearnRoomVO> list = timeLearnRoomService.list(type)
                .stream()
                .filter(r -> r.getUsedSeatsNum() < r.getTotalSeatsNum())
                .collect(Collectors.toList());
        if (list.isEmpty()) {
            return failState(TIME_LEARN_ROOM_IS_FULL, TIME_LEARN_ROOM_IS_FULL.getTips());
        }
        Random random = new Random();
        int n = random.nextInt(list.size());
        Long roomId = list.get(n).getId();
        return joinRoom(roomId);
    }

    @ApiOperation(value = "立即加入计时自习室-机器人专用")
    @ApiResponses(value = {
            @ApiResponse(code = 19503, message = "计时自习室没有该用户"),
            @ApiResponse(code = 19505, message = "用户在计时自习室学习中")
    })
    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @GetMapping(value = "/robot/joinNow")
    public ResultVO<TimeLearnRoomVO> joinNow_robot(@ApiParam(value = "计时自习室类型", required = true) @RequestParam Integer type) {

        List<TimeLearnRoomVO> list = timeLearnRoomService.list(type)
                .stream()
                .filter(r -> r.getUsedSeatsNum() < r.getTotalSeatsNum())
                .collect(Collectors.toList()).subList(0,5);
        if (list.isEmpty()) {
            return failState(TIME_LEARN_ROOM_IS_FULL, TIME_LEARN_ROOM_IS_FULL.getTips());
        }
        Random random = new Random();
        int n = random.nextInt(list.size());
        Long roomId = list.get(n).getId();
        return joinRoom(roomId);
    }


    @ApiOperation(value = "加入计时自习室-APP")
    @ApiResponses(value = {
            @ApiResponse(code = 19503, message = "计时自习室没有该用户"),
            @ApiResponse(code = 19505, message = "用户在计时自习室学习中")
    })
    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @GetMapping(value = "/join")
    public ResultVO<TimeLearnRoomVO> joinRoom(@ApiParam(value = "房间id", required = true) @RequestParam Long roomId) {

        Long userId = currentUserId();
        try {
            timeLearnRoomService.joinRoom(roomId, userId);
        } catch (UserTimeLearningException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_LEARNING, e.getMessage());
        } catch (UnRoomUserException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_NO_THIS_USER, e.getMessage());
        }

        TimeLearnRoomVO roomDetail = timeLearnRoomService.detail(roomId);
        if (Objects.isNull(roomDetail)) {
            throw new IllegalArgumentException("房间不存在 roomId = " + roomId);
        }
        return successState(roomDetail);
    }

    @ApiOperation(value = "入座-APP")
    @ApiResponses(value = {
            @ApiResponse(code = 19501, message = "计时自习室座位已经被占用"),
            @ApiResponse(code = 19502, message = "计时自习室座位不合法或不存在"),
            @ApiResponse(code = 19503, message = "计时自习室没有该用户，请先加入房间"),
            @ApiResponse(code = 19505, message = "用户在计时自习室学习中")
    })
    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @PostMapping(value = "/takeSeat")
    public ResultVO<TimeLearnRoomVO> takeSeat(@ApiParam(value = "入座信息", required = true) @RequestBody TakeSeatVO takeSeat) {

        Long userId = currentUserId();
        TimeLearnRoomVO roomDetail;
        try {
            TimeLearnRoomSeatsVO seat = timeLearnRoomService.takeSeat(takeSeat.getSeatId(), userId, takeSeat.getContentId(), takeSeat.getContent(), takeSeat.getMinutes(), takeSeat.getUseOtherApps());
            roomDetail = timeLearnRoomService.detail(seat.getRoomId());
        } catch (RoomSeatException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_SEAT_UNCHECK, e.getMessage());
        } catch (RoomSeatUsedException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_SEAT_USED);
        } catch (UserTimeLearningException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_LEARNING);
        } catch (UnRoomUserException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_NO_THIS_USER);
        }
        return successState(roomDetail);

    }

    @ApiOperation(value = "快速入座-APP")
    @ApiResponses(value = {
            @ApiResponse(code = 19503, message = "计时自习室没有该用户，请先加入房间"),
            @ApiResponse(code = 19505, message = "用户在计时自习室学习中"),
            @ApiResponse(code = 19506, message = "计时自习室座位已满")
    })
    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @PostMapping(value = "/takeSeatNow")
    public ResultVO<TimeLearnRoomVO> takeSeatNow(@ApiParam(value = "入座信息", required = true) @RequestBody TakeSeatVO takeSeat) {

        Long userId = currentUserId();
        if (Objects.isNull(takeSeat.getContentId())
                || StringUtils.isBlank(takeSeat.getContent())
                || Objects.isNull(takeSeat.getUseOtherApps())
                || Objects.isNull(takeSeat.getMinutes())
        ) {
            throw new IllegalArgumentException("参数不能为 null !  userId = " + userId + " contentId = " + takeSeat.getContentId() + " content = " + takeSeat.getContent() + " useOtherApps = " + takeSeat.getUseOtherApps() + " minute = " + takeSeat.getMinutes());
        }

        TimeLearnRoomUsersDO roomUsers = timeLearnRoomService.findByUserId(userId);
        if (Objects.isNull(roomUsers)) {
            return failState(TIME_LEARN_ROOM_NO_THIS_USER);
        }
        List<TimeLearnRoomSeatsVO> seats = timeLearnRoomService.allSeats(roomUsers.getRoomId())
                .stream()
                .filter(seat -> Objects.isNull(seat.getUserId()))
                .collect(Collectors.toList());

        if (seats.isEmpty()) {
            return failState(TIME_LEARN_ROOM_SEAT_IS_FULL);
        }

        TimeLearnRoomSeatsVO seat = seats.get(new Random().nextInt(seats.size()));
        takeSeat.setSeatId(seat.getId());
        ResultVO<TimeLearnRoomVO> result = takeSeat(takeSeat);
        if (result.getCode().equals(TIME_LEARN_ROOM_SEAT_USED.getCode())) {
            takeSeatNow(takeSeat);
        }
        return result;
    }


    @ApiOperation(value = "退出房间+离开座位-APP")
    @ApiResponses(value = {
            @ApiResponse(code = 19504, message = "计时自习室座位上没有该用户，请检查后再进行操作")
    })
    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @GetMapping(value = "/sitUpAndQuit")
    public ResultVO sitUp(@ApiParam(value = "房间id", required = true) @RequestParam Long roomId) {

        Long userId = currentUserId();
        try {
            timeLearnRoomService.sitUpAndQuit(roomId, userId);
        } catch (RoomSeatException e) {
            log.warn("{} userId = {}", e.getMessage(), userId);
            return successState(null);
        }
        return successState(null);
    }

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时自习室详情-APP", notes = "计时自习室详情")
    @GetMapping(value = "/detail")
    public ResultVO<TimeLearnRoomVO> detail(@ApiParam(value = "房间id", required = true) @RequestParam Long roomId) {
        TimeLearnRoomVO roomDetail = timeLearnRoomService.detail(roomId);
        return successState(roomDetail);
    }


    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "用户在自习室详情（内部）", notes = "用户在自习室详情")
    @GetMapping(value = "/detail-user")
    public ResultVO<TimeLearnRoomSeatsVO> detailUser(@ApiParam(value = "用户id", required = true) @RequestParam Long userId) {
        TimeLearnRoomSeatsDO roomDetail = timeLearnRoomService.getTimeLearnRoomSeatsByUserId(userId);
        return successState(TimeLearnRoomSeatsVO.from(roomDetail));
    }

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时自习室心跳-APP")
    @ApiResponses(value = {
            @ApiResponse(code = 19503, message = "计时自习室没有该用户，请重新加入房间")
    })
    @GetMapping(value = "/heart")
    public ResultVO heart(@ApiParam(value = "房间id", required = true) @RequestParam Long roomId) {

        Long userId = currentUserId();
        log.info("计时自习室心跳 roomId = {} userId = {}", roomId, userId);
        try {
            timeLearnRoomService.heart(roomId, userId);
        } catch (UnRoomUserException e) {
            log.warn("计时自习室心跳 {} userId = {}", e.getMessage(), userId);
            return failState(TIME_LEARN_ROOM_NO_THIS_USER);
        }
        return successState(null);
    }

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时自习室学习内容-APP")
    @GetMapping(value = "/contents")
    public ResultVO timeLearnContent() {

        Long userId = currentUserId();
        List<LearnContentVO> result = userClient.getContentList(userId).getData();
        return successState(result);
    }


    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时自习室-好友学习状态-APP")
    @GetMapping(value = "/room/friends/status")
    public ResultVO<LearnRoomFriendsVO> friendsStatus() {

        Long userId = currentUserId();
        List<Long> friends = userFriendClient.userFriendsList(userId).getData().stream().map(UserFriendVO::getFriendsUserId).collect(Collectors.toList());
        if (friends.size() < ROOM_MAX_FRIEND_NUM) {
            UserFriendsCondition condition = new UserFriendsCondition();
            condition.setUserId(userId);
            condition.setPageNumber(1);
            condition.setPageSize(ROOM_MAX_FRIEND_NUM);
            List<Long> follows = userFriendClient.follows(condition).getData().getRows().stream().map(UserFollowInfo::getFollowUserId).collect(Collectors.toList());
            friends.addAll(follows);
            //去重
            friends = friends.stream().distinct().collect(Collectors.toList());
        }

        if (!friends.isEmpty()) {
            List<UserVO> data = userClient.find(friends).getData()
                    .stream()
                    .peek(this::supply)
                    .sorted(Comparator.comparing(UserVO::getStatus).thenComparing(UserVO::getOnline).thenComparing(UserVO::getStartLearnTime).reversed())
                    .collect(Collectors.toList());
            long onlineNum = data.stream().filter(u -> LEARN_TIME_LEARNING.code.equals(u.getStatus()) || YN.YES.getCode() == u.getOnline()).count();

            LearnRoomFriendsVO friendList = LearnRoomFriendsVO
                    .builder()
                    .rows(data)
                    .total(data.size())
                    .onlineNum(onlineNum)
                    .build();
            return successState(friendList);
        }
        return successState(null);
    }

    private void supply(UserVO user) {
        if(YN.NO.getCode() == user.getOnline()){
            user.setStatus(NOMAL.code);
        }
        if (LEARN_TIME_LEARNING.code.equals(user.getStatus()) && Objects.nonNull(user.getRoomId())) {
            TimeLearnRoomDO roomInfo = timeLearnRoomService.getSimpleRoomInfo(user.getRoomId());
            if (Objects.nonNull(roomInfo)) {
                user.setRoomType(roomInfo.getType());
            }
        }

        //设置startLearnTime主要是为了排序
        if (Objects.nonNull(user.getStartLearnTime())) {
            return;
        }
        if (Objects.nonNull(user.getChannelId())) {
            user.setStartLearnTime(new Date(user.getChannelId()));
            return;
        }
        if (Objects.nonNull(user.getUpdateTime())) {
            user.setStartLearnTime(user.getUpdateTime());
            return;
        }
        user.setStartLearnTime(new Date());
    }

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时自习室-房间内所有的用户会话(内部)")
    @GetMapping(value = "/room/users")
    public ResultVO<List<DialogVO>> allRoomUsers(@ApiParam(value = "房间id", required = true) @RequestParam Long roomId) {
        List<DialogVO> users = timeLearnRoomDialogStore.all(roomId).stream().peek(DialogVO::getUserId).collect(Collectors.toList());
        return successState(users);
    }

    @AutoLog
    @PreAuthorize("isAuthenticated()")
    @ApiOperation(value = "计时自习室-根据房间id查询房间信息(内部)")
    @GetMapping(value = "/simple/roomInfo")
    public ResultVO<TimeLearnRoomVO> getSimpleRoomInfo(@ApiParam(value = "房间id", required = true) @RequestParam Long roomId) {
        TimeLearnRoomDO roomInfo = timeLearnRoomService.getSimpleRoomInfo(roomId);
        TimeLearnRoomVO room = TimeLearnRoomVO.from(roomInfo);
        return successState(room);
    }

    @AutoLog
    @ApiOperation(value = "计时自习室-计时学习总人数")
    @GetMapping(value = "/room/totalNum")
    public ResultVO<Integer> totalNum() {
        Integer usedSeatsNum = timeLearnRoomService.sumUsedSeatsNum();
        usedSeatsNum = usedSeatsNum * 2;
        return successState(usedSeatsNum);
    }

    @Autowired
    private TimeLearnRoomService timeLearnRoomService;
    @Autowired
    private UserClient userClient;
    @Autowired
    private UserFriendClient userFriendClient;
    @Autowired
    private TimeLearnRoomDialogStore timeLearnRoomDialogStore;
    //计时自习室好友列表显示最大人数
    private static final int ROOM_MAX_FRIEND_NUM = 300;

}
